package com.vegetable.common.aspect;

import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.fasterxml.jackson.databind.SerializationFeature;
import com.vegetable.common.annotation.PreventDuplicateSubmit;
import com.vegetable.modules.controller.AbstractController;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Component;
import org.springframework.util.DigestUtils;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.lang.reflect.Method;
import java.nio.charset.StandardCharsets;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.TimeUnit;
@Slf4j
@Aspect
@Component
public class DuplicateSubmitAspect extends AbstractController {

    private final StringRedisTemplate stringRedisTemplate;
    private static final String DUPLICATE_KEY_PREFIX = "SUBMIT_LOCK:";
    private final ObjectMapper objectMapper;

    public DuplicateSubmitAspect(StringRedisTemplate stringRedisTemplate, ObjectMapper objectMapper) {
        this.stringRedisTemplate = stringRedisTemplate;
        this.objectMapper = objectMapper;
    }

    @Around("@annotation(preventDuplicateSubmit)")
    public Object intercept(ProceedingJoinPoint joinPoint, PreventDuplicateSubmit preventDuplicateSubmit) throws Throwable {
        // 生成唯一请求标识（用户ID + 方法签名 + 参数摘要）
        String lockKey = buildLockKey(joinPoint);
        log.info("验证是否重复提交{}", lockKey);
        // 尝试获取 Redis 锁（setIfAbsent = SETNX）
        Boolean success = stringRedisTemplate.opsForValue().setIfAbsent(
                lockKey,
                "1",
                preventDuplicateSubmit.expireTime(),
                TimeUnit.SECONDS
        );

        if (Boolean.TRUE.equals(success)) {
            try {
                // 获取锁成功，执行业务逻辑
                return joinPoint.proceed();
            } finally {
                // 注意：不主动删除Key，等待自动过期（避免并发问题）
            }
        } else {
            // 获取锁失败，抛出重复提交异常
            throw new RuntimeException("请勿重复提交");
        }
    }

    /**
     * 生成分布式锁 Key
     */
    private String buildLockKey(ProceedingJoinPoint joinPoint) throws JsonProcessingException {
        MethodSignature signature = (MethodSignature) joinPoint.getSignature();
        Method method = signature.getMethod();
        String methodName = method.getName();
        String className = method.getDeclaringClass().getName();

        // 示例：组合用户ID（实际从Session/Token获取）
        Long userId = getUserId();
        // 参数摘要（避免参数过长）
        String paramsHash = normalizeArgs(joinPoint.getArgs());
        log.info("检查{}是否有重复的参数{}", userId, paramsHash);
        return DUPLICATE_KEY_PREFIX + userId + ":" + className + "." + methodName + ":" + paramsHash;
    }

    private String normalizeArgs(Object[] args) throws JsonProcessingException {
        List<Object> serializableArgs = new ArrayList<>();
        for (int i = 0; i < args.length; i++) {
            Object arg = args[i];
            // 过滤掉请求和响应对象
            if (arg instanceof HttpServletRequest || arg instanceof HttpServletResponse) {
                continue;
            }
            serializableArgs.add(arg);
        }
        // 将参数序列化为字符串
        List<String> argStrings = new ArrayList<>();
        for (Object arg : serializableArgs) {
            try {
                if (arg == null) {
                    argStrings.add("null");
                } else if (arg instanceof String || arg instanceof Number || arg instanceof Boolean) {
                    argStrings.add(arg.toString());
                } else {
                    // 对于对象，使用ObjectMapper序列化
                    argStrings.add(objectMapper.writeValueAsString(arg));
                }
            } catch (JsonProcessingException e) {
                // 如果序列化失败，使用toString()方法
                log.warn("参数序列化失败，使用toString方法: {}", arg, e);
                argStrings.add(arg.toString());
            }
        }
        // 将所有参数的字符串表示按顺序拼接
        String argStr = String.join("|", argStrings);
        log.info("待检查的参数集合:{}", argStr);
        return DigestUtils.md5DigestAsHex(argStr.getBytes(StandardCharsets.UTF_8));
    }
}